//==========================================================================
// Copyright (c) 2000-2006,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

/* camera driver in emulator.
 * Ver. 1.0 , By Leng Bo, 2005-3-2
 * Ver. 1.1 , By Leng Bo, 2005-8-30
 * Ver. 2.0,  Porting to Elastos2.0, 2005-10-21
 */

#include <windows.h>
#include <vfw.h>
#include <stdio.h>
#include <assert.h>

#ifdef _GNUC
#include "vfwgnu.h"
#endif
#include "framecapture.h"

#define APP_MAIN_WND L"SDL_app"
#define WM_CAPTURE_FRAME WM_USER + 97
#define WM_CAP_FINISH   WM_USER + 99

#define WAIT_TIME INFINITE  //5000
#define PHOTO_FRAMERATE 5 // Snap a frame every PHOTO_FRAMERATE frames

FrameCapture g_frameCap;

void FrameCapture::CapLock()
{
    WaitForSingleObject(m_hCaptureLock, (DWORD)INFINITE);
}

void FrameCapture::CapUnlock()
{
    ReleaseMutex(m_hCaptureLock);
}


LRESULT CALLBACK FrameCap_StatusCallbackProc(HWND hWnd, int nID, LPSTR lpStatusText)
{
    LRESULT lResult;
    CAPSTATUS capStatus ; // Current state of the capture window

    if (!g_frameCap.m_hWndCap) return FALSE;

    // Get the status of the capture window
    lResult = capGetStatus(g_frameCap.m_hWndCap, &capStatus, sizeof (CAPSTATUS));

    g_frameCap.m_uScreenWidth = capStatus.uiImageWidth;
    g_frameCap.m_uScreenHeight = capStatus.uiImageHeight;

    if (nID == 0) {
        return lResult;
    }

    wprintf(L"Status# %d: %s\n", nID, lpStatusText);

    return lResult;

}

LRESULT CALLBACK FrameCap_ErrorCallbackProc(HWND hWnd, int nErrID,LPSTR lpErrorText)
{
    if (!g_frameCap.m_hWndCap) return FALSE;

    if (nErrID == 0) return TRUE;

    wprintf(L"Error# %d   %s\n", nErrID, lpErrorText);

    return (LRESULT)TRUE;
}

LRESULT CALLBACK FrameCap_FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
    if (!g_frameCap.m_hWndCap) return FALSE;

    g_frameCap.CapLock();
    if (g_frameCap.m_bCapture && g_frameCap.m_CaptureFunc) {
        g_frameCap.CapUnlock();
        PostMessage(g_frameCap.m_hWndCap, WM_CAPTURE_FRAME, (WPARAM)lpVHdr->lpData, 0);
        return TRUE ;
    }
    g_frameCap.CapUnlock();

    return TRUE ;
}

//
// The main function of capture thread
//
static DWORD __stdcall FrameCap_CapThdFunc(LPVOID para)
{
    assert(g_frameCap.m_hWnd != NULL);

    CAPDRIVERCAPS capDriverCaps ; // Capabilities of the capture driver
    LPBITMAPINFO lpbi = NULL;
    DWORD dwSize;
    DWORD dwRet;

    MSG msg;
    BOOL bRet;
    BOOL bInit;

    g_frameCap.m_hWndCap = capCreateCaptureWindowW(
        L"Capture Window",
        WS_CHILD | WS_VISIBLE,
        1, 1, 1, 1,
        (HWND)g_frameCap.m_hWnd, (int) 0);
        printf("1**************************\n");
    if (NULL == g_frameCap.m_hWndCap) goto _fail;

    bInit = capSetCallbackOnError(g_frameCap.m_hWndCap, (FARPROC)FrameCap_ErrorCallbackProc);
    if (!bInit) {
        goto _fail;
    }

    bInit = capSetCallbackOnStatus(g_frameCap.m_hWndCap, (FARPROC)FrameCap_StatusCallbackProc);
    if (!bInit) {
        goto _fail;
    }

    bInit = capSetCallbackOnFrame(g_frameCap.m_hWndCap, (FARPROC)FrameCap_FrameCallbackProc);
    if (!bInit) {
        goto _fail;
    }

    // Connects a capture window to a capture driver
    bInit = capDriverConnect(g_frameCap.m_hWndCap, 0);
    if (!bInit) {
        goto _fail;
    }

    //Returns the hardware capabilities of the capture driver
    // currently connected to a capture window
    bInit = capDriverGetCaps(g_frameCap.m_hWndCap,&capDriverCaps,sizeof(CAPDRIVERCAPS));
    if (!bInit) {
        goto _fail;
    }

    dwSize = capGetVideoFormatSize(g_frameCap.m_hWndCap);
    if (0 == dwSize) {
        goto _fail;
    }

    lpbi = (LPBITMAPINFO)malloc(dwSize);
    dwRet = capGetVideoFormat(g_frameCap.m_hWndCap, lpbi, dwSize);
    if (0 == dwRet) {
        goto _fail;
    }

    // BUGBUG: Only support 24 bits color
    g_frameCap.m_uScreenWidth = lpbi->bmiHeader.biWidth;
    g_frameCap.m_uScreenHeight = lpbi->bmiHeader.biHeight;
    g_frameCap.m_uPixelBytes = lpbi->bmiHeader.biBitCount/8;
    assert(lpbi->bmiHeader.biBitCount == 24);
    free(lpbi);

    bInit = TRUE;
    ::SetEvent(g_frameCap.m_hCapThreadEvent);
    goto _msgloop;

_fail:
    if (g_frameCap.m_hWndCap) {
        capCaptureAbort(g_frameCap.m_hWndCap); // Stop capture
        capDriverDisconnect(g_frameCap.m_hWndCap); // Disconnect driver and capture window
        DestroyWindow(g_frameCap.m_hWndCap);
        g_frameCap.m_hWndCap = NULL;
    }

    if (NULL != lpbi)
        free(lpbi);
    return 0;

_msgloop:
    // This is a GUI thread, so it should have a message loop
    while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) {
        if (bRet == -1) {
            // handle the error and possibly exit
        }
        else {
            if (msg.message == WM_CAPTURE_FRAME) {
                g_frameCap.CapLock();
                if(g_frameCap.m_bCapture){
                    g_frameCap.m_CaptureFunc((void *)msg.wParam);
                }
                g_frameCap.CapUnlock();
            }
            else if (msg.message == WM_CAP_FINISH) {
                return msg.wParam;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    // Return the exit code to the system.
    return msg.wParam;
}

int FrameCapture::Initialize()
{
    m_CaptureFunc    = NULL;

    m_hWnd = FindWindowW(APP_MAIN_WND, NULL);
    assert(m_hWnd);

    //Initalize successful or fail
    m_hCapThreadEvent = ::CreateEventW(NULL,
                           FALSE,
                           FALSE,
                           NULL);

    if (m_hCapThreadEvent == NULL) {
        wprintf(L"CreateEventW for g_frameCap.m_hCapThreadEvent error!\n");
        return 0;
    }

    m_hCaptureLock = ::CreateMutex (NULL,
                           FALSE,
                           NULL);

    if (m_hCaptureLock == NULL) {
        wprintf(L"CreateMutex for g_frameCap.m_hCaptureLock error!\n");
        return 0;
    }

    DWORD id;
    m_hCapThd = CreateThread(NULL, 0, FrameCap_CapThdFunc, NULL, 0, &id);
    if (!m_hCapThd) {
        wprintf(L"Create capture thread error!\n");
        return 0;
    }

    DWORD dwReturn = WaitForSingleObjectEx(m_hCapThreadEvent, WAIT_TIME, FALSE);
    if (dwReturn == WAIT_TIMEOUT) {
        wprintf(L"Waiting for Initializing failed.\n");
        return 0;
    }

    if (!capPreviewRate(m_hWndCap, 66)) return FALSE; // Sets the frame display rate in preview mode
    if (!capPreview(m_hWndCap, TRUE)) return FALSE; // Enable preview mode

    return 1;

}

void FrameCapture::Destroy()
{
    capPreview(m_hWndCap, FALSE); // Disable preview mode

    m_bCapture = FALSE;
    m_CaptureFunc = NULL;

    capSetCallbackOnStatus(m_hWndCap, NULL);
    capSetCallbackOnError(m_hWndCap, NULL);
    capSetCallbackOnFrame(m_hWndCap, NULL);

    capCaptureAbort(m_hWndCap); // Stop capture
    capDriverDisconnect(m_hWndCap); // Disconnect driver and capture window

    PostMessage(g_frameCap.m_hWndCap, WM_CAP_FINISH, 0, 0);

    if (m_hWndCap) {
        DestroyWindow(m_hWndCap);
        m_hWndCap = NULL;
    }

    CloseHandle(m_hCapThreadEvent);

    CloseHandle(m_hCaptureLock);

    DWORD dwReturn = WaitForSingleObject(m_hCapThd, WAIT_TIME);
    if (dwReturn == WAIT_TIMEOUT) {
        wprintf(L"Waiting for quit failed.\n");
    }
    CloseHandle(m_hCapThd);
}

void FrameCapture::GetFrameDataInfo(unsigned long* puScreenWidth, unsigned long* puScreenHeight, unsigned int *puPixelBytes)
{
    if(NULL != puScreenWidth){
        *puScreenWidth = m_uScreenWidth;
    }

    if(NULL != puScreenHeight){
        *puScreenHeight = m_uScreenHeight;
    }

    if(NULL != puPixelBytes){
        *puPixelBytes = m_uPixelBytes;
    }
}

void FrameCapture::BeginCapture(FrameCaptureCallBack pFrameCapFunc)
{
    assert(NULL != pFrameCapFunc);

    CapLock();
    m_CaptureFunc = pFrameCapFunc;
    m_bCapture = TRUE;
    CapUnlock();

}

void FrameCapture::StopCapture()
{
    CapLock();
    m_bCapture = FALSE;
    m_CaptureFunc = NULL;
    CapUnlock();
}

#ifdef DBGCAPTURE

static DWORD __stdcall thd_createWindow(LPVOID para)
{
    WNDCLASS WndClass;
    MSG msg;
    HINSTANCE hInst =  GetModuleHandle(NULL);

    WndClass.style =  CS_BYTEALIGNCLIENT ;//CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    WndClass.lpfnWndProc = DefWindowProc;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hInstance = hInst;
    WndClass.hIcon = NULL;
    WndClass.hCursor =NULL;
    WndClass.hbrBackground = NULL;
    WndClass.lpszMenuName = NULL;
    WndClass.lpszClassName = APP_MAIN_WND;

    RegisterClass(&WndClass);

    RECT rect;
    ZeroMemory(&rect, sizeof(RECT));
    rect.right = 200;
    rect.bottom = 200;
    AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, TRUE, 0);

    HWND hwndElauraMain = CreateWindowW(APP_MAIN_WND,
                                     L"test",
                                     WS_OVERLAPPEDWINDOW,
                                     CW_USEDEFAULT, CW_USEDEFAULT,
                                     rect.left + rect.right,
                                     rect.top + rect.bottom,
                                     NULL,
                                     NULL,
                                     hInst,
                                     NULL );
    g_frameCap.m_hWnd = hwndElauraMain;

    ShowWindow(hwndElauraMain, SW_NORMAL);
    UpdateWindow(hwndElauraMain);
    BOOL bRet;
    while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) {
        if (bRet == -1) {
            // handle the error and possibly exit
        }
        else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 0;
}


main(){

    DWORD id;
    HANDLE hThd = CreateThread(NULL, 0, thd_createWindow, NULL, 0, &id);
    if (!hThd) {
        wprintf(L"Create thread error!\n");
        return 0;
    }


    Sleep(1000);

    g_frameCap.Initialize();


    Sleep(10000);



    return 0;


}
#endif

